package com.zxy.ziems.server.alarm.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.zxy.btp.common.response.PageResult;
import com.zxy.btp.redis.utils.RedisUtil;
import com.zxy.btp.web.util.TokenUtils;
import com.zxy.ziems.common.mqtt.template.MqttClientTemplate;
import com.zxy.ziems.common.pojo.MeasureDataBean;
import com.zxy.ziems.server.alarm.convertor.AlarmConvertor;
import com.zxy.ziems.server.alarm.enums.AlarmProcessStatusEnum;
import com.zxy.ziems.server.alarm.enums.AlarmTypeEnum;
import com.zxy.ziems.server.alarm.enums.AlarmTypeParamEnum;
import com.zxy.ziems.server.alarm.enums.GasAlarmStatusEnum;
import com.zxy.ziems.server.alarm.mapper.AlarmRecordMapper;
import com.zxy.ziems.server.alarm.pojo.dto.AlarmDealCountDTO;
import com.zxy.ziems.server.alarm.pojo.dto.AlarmProcessRecordDTO;
import com.zxy.ziems.server.alarm.pojo.dto.AlarmRecordEvent;
import com.zxy.ziems.server.alarm.pojo.dto.AlarmTypeCountDTO;
import com.zxy.ziems.server.alarm.pojo.entity.*;
import com.zxy.ziems.server.alarm.pojo.param.AlarmRecordPageTypeParam;
import com.zxy.ziems.server.alarm.pojo.param.DeviceAlarmPageParam;
import com.zxy.ziems.server.alarm.pojo.vo.*;
import com.zxy.ziems.server.alarm.service.*;
import com.zxy.ziems.server.constant.DateType;
import com.zxy.ziems.server.constant.enums.DictCodeEnum;
import com.zxy.ziems.server.device.pojo.dto.DataTypeDTO;
import com.zxy.ziems.server.device.pojo.dto.DeviceTypeDTO;
import com.zxy.ziems.server.device.pojo.entity.*;
import com.zxy.ziems.server.device.pojo.enums.DeviceAlarmStatus;
import com.zxy.ziems.server.device.pojo.param.DeviceQueryParam;
import com.zxy.ziems.server.device.pojo.vo.DeviceVO;
import com.zxy.ziems.server.device.service.*;
import com.zxy.ziems.server.domain.DictItem;
import com.zxy.ziems.server.domain.User;
import com.zxy.ziems.server.domain.UserRoleRel;
import com.zxy.ziems.server.dto.DateDto;
import com.zxy.ziems.server.message.MessageService;
import com.zxy.ziems.server.mybatis.core.query.QueryWrapperX;
import com.zxy.ziems.server.mybatis.core.query.WrapperX;
import com.zxy.ziems.server.mybatis.core.service.ServiceImplX;
import com.zxy.ziems.server.service.DictItemService;
import com.zxy.ziems.server.service.UserRoleRelService;
import com.zxy.ziems.server.service.UserService;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.DateUtils;
import com.zxy.ziems.server.vo.ChartVo;
import com.zxy.ziems.server.vo.ChartYAxisVo;
import io.netty.buffer.ByteBuf;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zxy.ziems.server.alarm.enums.AlarmProcessStatusEnum.GOING;
import static com.zxy.ziems.server.alarm.enums.AlarmProcessStatusEnum.MANUAL_RESET;

/**
 * 告警类型
 *
 * @author chdzq
 * @create 2023/4/12
 */
@Service
@Slf4j
public class AlarmRecordServiceImpl extends ServiceImplX<AlarmRecordMapper, AlarmRecordEntity> implements AlarmRecordService {

    @Value("${zxy.topic.push-alarm-data}")
    private String PUSH_TOPIC;


    @Autowired
    private MessageService messageService;

    @Autowired
    private DevicePropertyAlarmService devicePropertyAlarmService;


    @Autowired
    private AlarmRuleService alarmRuleService;

    @Autowired
    private AlarmRuleRangeService alarmRuleRangeService;

    @Autowired
    private AlarmRuleRoleService alarmRuleRoleService;

    @Autowired
    private UserRoleRelService userRoleRelService;

    @Autowired
    private DataTypeService dataTypeService;

    @Autowired
    private DeviceTypeService deviceTypeService;

    @Autowired
    private DictItemService dictItemService;

    private MqttClientTemplate mqttClientTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    public void setMqttClientTemplate(MqttClientTemplate mqttClientTemplate) {
        this.mqttClientTemplate = mqttClientTemplate;
    }


    @Override
    public void add(AlarmRecordEvent event) {
        log.info("[告警记录]发送消息:{}", event);
        DeviceEntity deviceEntity = deviceService.getById(event.getDeviceId());
        if (Objects.isNull(deviceEntity)) {
            log.error("[告警记录]:设备：{}不存在", event.getAlarmTypeId());
            return;
        }
        event.setDeviceCode(deviceEntity.getCode());
        event.setDeviceTypeCode(deviceEntity.getTypeCode());

        AlarmRecordEntity entity = AlarmConvertor.INSTANCE.alarmRecordDTOConvertor2DO(event);
        save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processAlarmRecord(AlarmProcessRecordDTO dto) {
        AlarmRecordEntity alarmRecordEntity = getById(dto.getAlarmRecordId());
        AssertUtil.nonNull(alarmRecordEntity, "告警记录不存在");
        AssertUtil.isFalse(Objects.equals(1, alarmRecordEntity.getProcessStatus()), "告警记录已经被处理了");

        AlarmProcessRecordEntity processRecordEntity = processRecordService.add(dto);

        String userId = TokenUtils.getUserIdByToken();
        LambdaUpdateWrapper<AlarmRecordEntity> updateWrapper = WrapperX.<AlarmRecordEntity>lambdaUpdate()
                .eq(AlarmRecordEntity::getId, dto.getAlarmRecordId())
                .set(AlarmRecordEntity::getProcessId, processRecordEntity.getId())
                .set(AlarmRecordEntity::getProcessContent, processRecordEntity.getProcessContent())
                .set(AlarmRecordEntity::getProcessUserId, userId)
                .set(AlarmRecordEntity::getProcessTime, LocalDateTime.now())
                .set(AlarmRecordEntity::getProcessStatus, MANUAL_RESET.getCode());
        update(updateWrapper);
        deviceService.updateAlarmStatus(alarmRecordEntity.getDeviceId(), DeviceAlarmStatus.NORMAL.getCode());
    }

    @Override
    public PageResult<AlarmRecordVO> pageListBy(AlarmRecordPageTypeParam param) {

        if (param.getAlarmModelId() != null) {
            List<String> code = AlarmTypeParamEnum.getCodeByValue(param.getAlarmModelId());
            param.setAlarmModelId(null);
            param.setDeviceTypeCodes(code);
        }

        PageResult<AlarmRecordEntity> pageResult = customPage(param, (page, aParam) -> baseMapper.queryPageList(page, aParam));
        if (CollectionUtil.isEmpty(pageResult.getList())) {
            return new PageResult<AlarmRecordVO>(0L, new ArrayList<>(0));
        }


        Set<String> deviceIdSet = new HashSet<>();
        Set<String> alarmTypeIdSet = new HashSet<>();
        Set<String> processUserIdSet = new HashSet<>();
        Set<String> dataTypeIdSet = new HashSet<>();
        Set<String> deviceCodeSet = new HashSet<>();
        for (AlarmRecordEntity entity : pageResult.getList()) {
            if (StrUtil.isNotBlank(entity.getDeviceId())) {
                deviceIdSet.add(entity.getDeviceId());
            }
            if (StrUtil.isNotBlank(entity.getDeviceDataTypeId())) {
                dataTypeIdSet.add(entity.getDeviceDataTypeId());
            }
            if (StrUtil.isNotBlank(entity.getAlarmTypeId())) {
                alarmTypeIdSet.add(entity.getAlarmTypeId());
            }
            if (Objects.equals(1, entity.getProcessStatus()) && StrUtil.isNotBlank(entity.getProcessUserId())) {
                processUserIdSet.add(entity.getProcessUserId());
            }
            String deviceCode = getDeviceCode(entity.getAlarmContent());
            if (StrUtil.isNotBlank(deviceCode)) {
                deviceCodeSet.add(deviceCode);
            }
        }

        Map<String, DeviceEntity> deviceEntityMap = deviceService.listByIdListAndDelete(deviceIdSet).stream()
                .collect(Collectors.toMap(DeviceEntity::getId, (a) -> a));

        Map<String, DeviceEntity> deviceCodeEntityMap = deviceService.listByCodeListAndDelete(deviceCodeSet).stream()
                .collect(Collectors.toMap(DeviceEntity::getCode, (a) -> a));

        Map<String, AlarmTypeEntity> alarmTypeMap = alarmTypeService.listByIds(alarmTypeIdSet).stream()
                .collect(Collectors.toMap(AlarmTypeEntity::getId, (a) -> a));

        Map<String, DataTypeEntity> dataTypeEntityMap = dataTypeService.listByIdList(dataTypeIdSet).stream().collect(Collectors.toMap(DataTypeEntity::getId, v -> v));

        Map<String, User> userMap = userService.listByIds(processUserIdSet).stream()
                .collect(Collectors.toMap(User::getUserId, (a) -> a));

        Map<String, Map<String, DevicePropertyEntity>> devicePropertyMap = devicePropertyService.listByDeviceIds(deviceIdSet)
                .stream()
                .collect(
                        Collectors.groupingBy(
                                DevicePropertyEntity::getDeviceId,
                                Collectors.toMap(DevicePropertyEntity::getDataTypeId, (a) -> a)
                        )
                );
        List<AlarmRecordVO> list = pageResult.getList()
                .stream()
                .map((entity) -> {
                    DeviceEntity deviceEntity = StrUtil.isBlank(entity.getDeviceId()) ? null : deviceEntityMap.get(entity.getDeviceId());
                    AlarmTypeEntity alarmTypeEntity = StrUtil.isBlank(entity.getAlarmTypeId()) ? null : alarmTypeMap.get(entity.getAlarmTypeId());
                    User processUser = StrUtil.isBlank(entity.getProcessUserId()) ? null : userMap.get(entity.getProcessUserId());
                    DataTypeEntity dataTypeEntity = StrUtil.isBlank(entity.getDeviceDataTypeId()) ? null : dataTypeEntityMap.get(entity.getDeviceDataTypeId());
                    DevicePropertyEntity propertyEntity;
//                    if (StrUtil.isBlank(entity.getDeviceDataTypeId())) {
//                        dataTypeEntity = null;
//                    } else {
//                        propertyEntity = devicePropertyMap.getOrDefault(entity.getDeviceId(), new HashMap<>())
//                                .get(entity.getDeviceDataTypeId());
//                    }


                    if (entity.getDeviceTypeCode().equals(AlarmTypeParamEnum.FIRE.getCode())) {
                        String deviceCode = getDeviceCode(entity.getAlarmContent());
                        deviceEntity = StrUtil.isBlank(deviceCode) ? new DeviceEntity() : deviceCodeEntityMap.get(deviceCode);
                    }


                    final AlarmRecordVO alarmRecordVO = AlarmConvertor.INSTANCE.alarmRecordDOConvertor2VO(
                            entity,
                            deviceEntity,
                            dataTypeEntity,
                            alarmTypeEntity,
                            processUser);

                    if (entity.getDeviceTypeCode().equals(AlarmTypeParamEnum.FIRE.getCode())) {
                        String deviceCode = getDeviceCode(entity.getAlarmContent());
                        if (deviceCode != null && deviceEntity != null && deviceEntity.getName() != null) {
                            String deviceName = deviceEntity.getName();
                            String alarmContent = repalce(deviceName, entity.getAlarmContent());
                            alarmRecordVO.setAlarmContent(alarmContent);
                        } else {
                            alarmRecordVO.setDevice(new DeviceVO<>());
                        }


                    }

                    if (alarmRecordVO.getDevice() == null) {
                        alarmRecordVO.setDevice(new DeviceVO<>());
                    }
                    return alarmRecordVO;

                }).toList();
        return new PageResult<>(pageResult.getTotal(), list);
    }

    public String repalce(String replacement, String input) {
//        String input = "消防报警：事件《火灾报警》探测器《N003L03M091》区域《Zone Z052》类型《传统烟感》";
//        String replacement = "NEW_CONTENT"; // 要替换成的新内容

        // 找到第一个《的位置
        int firstIndex = input.indexOf("《");
        if (firstIndex == -1) {
            System.out.println("没有找到《");
            return input;
        }

        // 从第一个《后面开始找第二个《
        int secondIndex = input.indexOf("《", firstIndex + 1);
        if (secondIndex == -1) {
            System.out.println("没有找到第二个《");
            return input;
        }

        // 找到对应的》
        int endIndex = input.indexOf("》", secondIndex);
        if (endIndex == -1) {
            System.out.println("没有找到对应的》");
            return input;
        }

        // 提取并替换内容
        String before = input.substring(0, secondIndex); // 第二个《之前的内容
        String after = input.substring(endIndex + 1); // 第二个》之后的内容
        String result = before + "《" + replacement + "》" + after; // 拼接替换后的字符串

        System.out.println("替换后的内容是: " + result);
        return result;
    }


    public String getDeviceCode(String input) {
//        String input = "消防报警：事件《火灾报警》探测器《N003L03M091》区域《Zone Z052》类型《传统烟感》";

        if (StringUtils.isBlank(input)) {
            return null;
        }

        // 找到第二个《的位置
        int startIndex = input.indexOf("《", input.indexOf("《") + 1); // 从第一个《后面开始找
        if (startIndex == -1) {
            System.out.println("没有找到第二个《");
            return null;
        }

        // 找到对应的》的位置
        int endIndex = input.indexOf("》", startIndex);
        if (endIndex == -1) {
            System.out.println("没有找到对应的》");
            return null;
        }

        // 提取内容
        String content = input.substring(startIndex + 1, endIndex);
        System.out.println("提取的内容是: " + content);
        return content;
    }

    @Override
    public PageResult<AlarmRecordVO> pageByDevice(DeviceAlarmPageParam param) {
        PageResult<AlarmRecordEntity> pageResult = customPage(param, (page, aParam) -> baseMapper.queryPageListByDevice(page, aParam));
        if (CollectionUtil.isEmpty(pageResult.getList())) {
            return new PageResult<AlarmRecordVO>(0L, new ArrayList<>(0));
        }

        Set<String> deviceIdSet = new HashSet<>();
        Set<String> alarmTypeIdSet = new HashSet<>();
        for (AlarmRecordEntity entity : pageResult.getList()) {
            if (StrUtil.isNotBlank(entity.getDeviceId())) {
                deviceIdSet.add(entity.getDeviceId());
            }
            if (StrUtil.isNotBlank(entity.getAlarmTypeId())) {
                alarmTypeIdSet.add(entity.getAlarmTypeId());
            }
        }

        Map<String, DeviceEntity> deviceEntityMap = deviceService.listByIdList(deviceIdSet).stream()
                .collect(Collectors.toMap(DeviceEntity::getId, (a) -> a));

        Map<String, AlarmTypeEntity> alarmTypeMap = alarmTypeService.listByIds(alarmTypeIdSet).stream()
                .collect(Collectors.toMap(AlarmTypeEntity::getId, (a) -> a));

        Map<String, Map<String, DevicePropertyEntity>> devicePropertyMap = devicePropertyService.listByDeviceIds(deviceIdSet)
                .stream()
                .collect(
                        Collectors.groupingBy(
                                DevicePropertyEntity::getDeviceId,
                                Collectors.toMap(DevicePropertyEntity::getDataTypeId, (a) -> a)
                        )
                );

        List<AlarmRecordVO> list = pageResult.getList()
                .stream()
                .map((entity) -> {
                    DeviceEntity deviceEntity = StrUtil.isBlank(entity.getDeviceId()) ? null : deviceEntityMap.get(entity.getDeviceId());
                    AlarmTypeEntity alarmTypeEntity = StrUtil.isBlank(entity.getAlarmTypeId()) ? null : alarmTypeMap.get(entity.getAlarmTypeId());
                    DevicePropertyEntity propertyEntity;
                    if (StrUtil.isBlank(entity.getDeviceDataTypeId()) || StrUtil.isBlank(entity.getDeviceId())) {
                        propertyEntity = null;
                    } else {
                        propertyEntity = devicePropertyMap.getOrDefault(entity.getDeviceId(), new HashMap<>())
                                .get(entity.getDeviceDataTypeId());
                    }
                    return AlarmConvertor.INSTANCE.alarmRecordDOConvertor2VO(
                            entity,
                            deviceEntity,
                            propertyEntity,
                            alarmTypeEntity,
                            null);
                }).toList();
        return new PageResult<>(pageResult.getTotal(), list);
    }

    @Override
    public AlarmRecordVO viewBy(String alarmId) {
        AlarmRecordEntity record = getById(alarmId);
        AssertUtil.nonNull(record, "记录不存在");

        DeviceEntity deviceEntity = null;
        if (StrUtil.isNotBlank(record.getDeviceId())) {
            deviceEntity = deviceService.getById(record.getDeviceId());
        }

        AlarmTypeEntity alarmTypeEntity = null;
        if (StrUtil.isNotBlank(record.getAlarmTypeId())) {
            alarmTypeEntity = alarmTypeService.getById(record.getAlarmTypeId());
        }

        User processUser = null;
        if (StrUtil.isNotBlank(record.getProcessUserId())) {
            processUser = userService.getById(record.getProcessUserId());
        }

        DevicePropertyEntity devicePropertyEntity = devicePropertyService.getBy(record.getDeviceId(), record.getDeviceDataTypeId());

        return AlarmConvertor.INSTANCE.alarmRecordDOConvertor2VO(
                record,
                deviceEntity,
                devicePropertyEntity,
                alarmTypeEntity,
                processUser);
    }

    /**
     * 安全报警处理统计
     *
     * @param startDate   开始时间
     * @param endDate     结束时间
     * @param modelTypeId 类型（气体，消防等）
     */
    @Override
    public List<AlarmDealCountVO> dealCount(String startDate, String endDate, String modelTypeId, String type) {
        List<String> deviceTypeCode = AlarmTypeParamEnum.getCodeByValue(modelTypeId);


        List<AlarmDealCountDTO> alarmTypeCountVOList = new ArrayList<>();
        if (type.equals(DateType.DAY)) {
            alarmTypeCountVOList = baseMapper.dealDayCount(startDate, endDate, deviceTypeCode);
        } else if (type.equals(DateType.MONTH)) {
            alarmTypeCountVOList = baseMapper.dealMonthCount(startDate, endDate, deviceTypeCode);
        } else if (type.equals(DateType.YEAR)) {
            alarmTypeCountVOList = baseMapper.dealYearCount(startDate, endDate, deviceTypeCode);
        }


        final Map<Integer, Long> stringStringMap = alarmTypeCountVOList.stream().collect(Collectors.toMap(m -> m.getProcessStatus(), m -> m.getCount()));


        List<AlarmDealCountVO> alarmDealCountVOList = Arrays.stream(AlarmProcessStatusEnum.values()).map(m -> {
            Long count = stringStringMap.get(m.getCode());
            AlarmDealCountVO alarmDealCountVO = new AlarmDealCountVO();
            alarmDealCountVO.setCount(count == null ? 0L : count);
            alarmDealCountVO.setProcessStatusName(m.getInfo());
            return alarmDealCountVO;
        }).collect(Collectors.toList());


        return alarmDealCountVOList;
    }

    /**
     * 安全报警处理统计
     *
     * @param startDate   开始时间
     * @param endDate     结束时间
     * @param modelTypeId 类型（气体，消防等）
     */
    @Override
    public ChartVo typeCount(String startDate, String endDate, String modelTypeId, String queryType) {
        List<String> deviceTypeCodes = AlarmTypeParamEnum.getCodeByValue(modelTypeId);
        List<AlarmTypeCountDTO> alarmTypeCountDTOList = new ArrayList<>();


        if (queryType.equals(DateType.DAY)) {
            queryType = DateType.MONTH;
            alarmTypeCountDTOList = baseMapper.typeDayCount(startDate, endDate, deviceTypeCodes);
        } else if (queryType.equals(DateType.MONTH)) {
            queryType = DateType.YEAR;
            alarmTypeCountDTOList = baseMapper.typeMonthCount(startDate, endDate, deviceTypeCodes);
        } else if (queryType.equals(DateType.YEAR)) {
            queryType = DateType.HIS_YEAR;
            alarmTypeCountDTOList = baseMapper.typeYearCount(startDate, endDate, deviceTypeCodes);
        }

        //根据查询条件计算起止时间
        DateDto dateDto = DateUtils.dateQueryConvertNo(queryType, startDate, endDate, false);
        //构建x轴
        List<LocalDateTime> list = DateUtils.dateToList(queryType, dateDto.getStartDay(), dateDto.getEndDay());

//        //根据查询条件计算起止时间
//        DateDto dateDto = DateUtils.dateQueryConvert(queryType, startDate, endDate, false);
//        //构建x轴
//        List<LocalDateTime> list = DateUtils.dateToList(queryType, dateDto.getStartDay(), dateDto.getEndDay());
        ChartVo chartVo = new ChartVo();
        chartVo.setXAxisList(DateUtils.xAxisConvert(queryType, list));

        Map<Integer, List<AlarmTypeCountDTO>> AlarmTypeCountMap =
                alarmTypeCountDTOList.stream().collect(Collectors.groupingBy(m -> m.getProcessStatus()));

        List<ChartYAxisVo> chartYList = new ArrayList<>();

        AlarmProcessStatusEnum.values();

        for (AlarmProcessStatusEnum alarmProcessStatusEnum : AlarmProcessStatusEnum.values()) {
            String processStatus = alarmProcessStatusEnum.getInfo();
            List<AlarmTypeCountDTO> alarmTypeCountDTOList1 = AlarmTypeCountMap.get(alarmProcessStatusEnum.getCode());


            //判断是否需要返回空值
            if (alarmTypeCountDTOList1 == null || CollectionUtil.isEmpty(alarmTypeCountDTOList1)) {
                chartYList.add(buildEmptyChartYAxisVo(list.size(), processStatus));
                continue;
            }


//            Map<String, List<AlarmTypeCountDTO>> alarmTypeCountList = alarmTypeCountDTOList1.stream().collect(Collectors.groupingBy(m -> m.getDate()));

//            multipleProcess(dateDtos, classifyDeviceProperty.getMultiple());

            //按照查询类型和数据类型对数据时间进行处理，并分组，将数据处理为x轴需要的格式进行匹配
            Map<String, List<AlarmTypeCountVO>> thisMap = buildDataByDataType(alarmTypeCountDTOList1, processStatus);
            List<BigDecimal> yValueList = new ArrayList<>();
            for (String dateTimeStr : chartVo.getXAxisList()) {
                //本周期数据
                setYVal(thisMap, yValueList, dateTimeStr);
            }
            ChartYAxisVo chartYAxisVo = new ChartYAxisVo();
            chartYAxisVo.setName(alarmProcessStatusEnum.getInfo());
            chartYAxisVo.setData(yValueList);
            chartYAxisVo.setType("bar");
//            chartYAxisVo.setStack(alarmStatusEnum.getId());
            chartYList.add(chartYAxisVo);
        }


        chartVo.setYAxisList(chartYList);
        return chartVo;

    }

    @Override
    public List<AlarmRecordDeviceVO> listByDeviceIds(Collection<? extends Serializable> deviceIdList) {

        if (deviceIdList.isEmpty()) {
            return new ArrayList<>();
        }

        List<AlarmRecordEntity> alarmRecordEntityList = baseMapper.selectListByDeviceIds(deviceIdList);

        List<AlarmRecordDeviceVO> alarmRecordDeviceVOList = alarmRecordEntityList.stream().map(entity -> {
            AlarmRecordDeviceVO alarmRecordDeviceVO = AlarmConvertor.INSTANCE.alarmRecordDOConvertorDeviceVO(entity);
            return alarmRecordDeviceVO;
        }).collect(Collectors.toList());

        return alarmRecordDeviceVOList;
    }

    private static void setYVal(Map<String, List<AlarmTypeCountVO>> thisMap, List<BigDecimal> thisYValueList, String dateTimeStr) {
        if (thisMap.containsKey(dateTimeStr)) {
            List<AlarmTypeCountVO> pointDataListByDate = thisMap.get(dateTimeStr);
            if (!pointDataListByDate.isEmpty()) {
                thisYValueList.add(new BigDecimal(pointDataListByDate.get(0).getCount()));
            } else {
                thisYValueList.add(BigDecimal.ZERO);
            }
        } else {
            thisYValueList.add(BigDecimal.ZERO);
        }
    }

    private Map<String, List<AlarmTypeCountVO>> buildDataByDataType(List<AlarmTypeCountDTO> pointDataList, String processStatusName) {
        //如果查询的不是日数据，且是自动的点位，需要把数据时间转换为天进行分组
        Map<String, List<AlarmTypeCountVO>> map = new HashMap<>();
        if (CollectionUtil.isEmpty(pointDataList)) {
            return map;
        }
        for (AlarmTypeCountDTO pointData : pointDataList) {
            AlarmTypeCountVO alarmDealCountVO = new AlarmTypeCountVO();
            alarmDealCountVO.setProcessStatusName(processStatusName);
            alarmDealCountVO.setCount(pointData.getCount());
            alarmDealCountVO.setDate(pointData.getDate());

            String dateStr = pointData.getDate();
            if (map.containsKey(dateStr)) {
                List<AlarmTypeCountVO> dataList = map.get(dateStr);
                dataList.add(alarmDealCountVO);
            } else {
                List<AlarmTypeCountVO> newList = new ArrayList<>();
                newList.add(alarmDealCountVO);
                map.put(dateStr, newList);
            }
        }
        return map;
    }


    @Override
    public void storageAlarmByAlamRule(List<MeasureDataBean> list) {
        log.info("");
        List<AlarmRecordEntity> alarmRecordEntityList = new ArrayList<>();
        for (MeasureDataBean measureData : list) {
            List<DevicePropertyAlarmEntity> devicePropertyAlarmList = devicePropertyAlarmService
                    .listByDeviceId(measureData.getDeviceId(), measureData.getDataTypeId());
            DeviceEntity deviceEntity = deviceService.getById(measureData.getDeviceId());
            Boolean statusFlag = Boolean.TRUE;
            for (DevicePropertyAlarmEntity propertyAlarm : devicePropertyAlarmList) {
                StringJoiner cacheKey = new StringJoiner(":");
                //判断是否为报警，报警时记录报警，非报警恢复为设备为正常状态
                if (judgeRangeRule(measureData.getMeasureValue(), propertyAlarm.getMinValue(), propertyAlarm.getMaxValue())) {
                    AlarmTypeEntity alarmType = alarmTypeService.getById(propertyAlarm.getAlarmTypeId());
                    AlarmRecordEntity entity = new AlarmRecordEntity();
                    entity.setId(IdWorker.getIdStr());
                    entity.setDeviceId(measureData.getDeviceId());
                    entity.setDeviceCode(deviceEntity.getCode());
                    entity.setDeviceTypeCode(deviceEntity.getTypeCode());
                    entity.setDeviceDataTypeId(propertyAlarm.getDeviceDataTypeId());
                    entity.setAlarmTypeId(alarmType.getId());
                    entity.setAlarmTime(measureData.getMeasureTime());
                    entity.setAlarmValue(new BigDecimal(measureData.getMeasureValue()));
                    StringJoiner alarmContent = new StringJoiner("，");
                    alarmContent.add(Optional.ofNullable(entity.getAlarmTime()).map(x -> x.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).orElse(""));
                    alarmContent.add(deviceEntity.getName());
                    alarmContent.add(alarmType.getName());
                    alarmContent.add("检测值: " + entity.getAlarmValue());
                    entity.setAlarmContent(alarmContent.toString());
                    entity.setProcessStatus(AlarmProcessStatusEnum.GOING.getCode());
                    cacheKey.add("ALARM_RECORD");
                    cacheKey.add(entity.getDeviceId());
                    cacheKey.add(entity.getDeviceDataTypeId());
                    cacheKey.add(entity.getAlarmTypeId());
                    statusFlag = Boolean.FALSE;
                    if (!redisUtil.hasKey(cacheKey.toString())) {
                        alarmRecordEntityList.add(entity);
                        //推送报警消息
                        this.pushAlarmMsgByAlamRule(entity);
                        deviceService.updateAlarmStatus(measureData.getDeviceId(), DeviceAlarmStatus.ALARM.getCode());
                        redisUtil.set(cacheKey.toString(), entity.getAlarmValue(), 5L, TimeUnit.MINUTES);
                    }
                }
            }
            //未匹配到报警规则，则代表设备恢复正常
            if (statusFlag) {
                deviceService.updateAlarmStatus(measureData.getDeviceId(), DeviceAlarmStatus.NORMAL.getCode());
                this.updateProcessStatusByDeviceId(measureData.getDeviceId(), AlarmProcessStatusEnum.CLEAR.getCode());
            }
        }
        saveBatch(alarmRecordEntityList);

    }

    public void updateProcessStatusByDeviceId(String deviceId, Integer processStatus) {
        LambdaUpdateWrapper<AlarmRecordEntity> updateWrapper = WrapperX.<AlarmRecordEntity>lambdaUpdate()
                .eq(AlarmRecordEntity::getDeviceId, deviceId)
                .set(AlarmRecordEntity::getProcessUserId, 0)
                .set(AlarmRecordEntity::getProcessTime, LocalDateTime.now())
                .set(AlarmRecordEntity::getProcessStatus, processStatus);
        update(updateWrapper);
    }

    @Override
    public void pushAlarmMsgByAlamRule(AlarmRecordEntity alarmRecord) {
        List<AlarmRuleRangeEntity> ruleRangeEntities = alarmRuleRangeService
                .listByAlarmType(alarmRecord.getDeviceTypeCode(), alarmRecord.getAlarmTypeId());
        List<AlarmRuleEntity> ruleEntities = alarmRuleService.listByIds(ruleRangeEntities.stream().map(AlarmRuleRangeEntity::getRuleId).collect(Collectors.toSet()));
        for (AlarmRuleEntity ruleEntity : ruleEntities) {
            AlarmMsgVo msgVo = new AlarmMsgVo();
            msgVo.setAlarmRuleName(ruleEntity.getAlarmRuleName());
            msgVo.setAlarmType(ruleEntity.getAlarmType());
            msgVo.setDeviceId(alarmRecord.getDeviceId());
            msgVo.setDeviceCode(alarmRecord.getDeviceCode());
            msgVo.setDeviceTypeCode(alarmRecord.getDeviceTypeCode());
            msgVo.setDeviceDataTypeId(alarmRecord.getDeviceDataTypeId());
            msgVo.setAlarmValue(alarmRecord.getAlarmValue());
            msgVo.setAlarmContent(alarmRecord.getAlarmContent());
            msgVo.setAlarmTime(alarmRecord.getAlarmTime());
            msgVo.setAlarmTypeId(alarmRecord.getAlarmTypeId());
            List<AlarmRuleRoleEntity> roleEntities = alarmRuleRoleService.listByRuleId(ruleEntity.getId());
            Set<String> roleIdSet = roleEntities.stream().map(AlarmRuleRoleEntity::getRoleId).collect(Collectors.toSet());
            List<UserRoleRel> userRoleRels = userRoleRelService.listByRoleIds(roleIdSet);
            Set<String> userIds = userRoleRels.stream().map(UserRoleRel::getRoleId).collect(Collectors.toSet());
            for (String userId : userIds) {
                mqttClientTemplate.publishPayload(PUSH_TOPIC + "/" + userId, msgVo);
            }
        }


    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addFireRecord(ByteBuf byteBuf) throws Exception {
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        String res = new String(bytes, "UTF-16BE");
        String collect = Arrays.stream(res.split("  ")).filter(m -> m != null && !m.trim().equals("")).collect(Collectors.joining(" "));
        log.info("channelRead===msg=={}", collect);
        // 获取40个字符（事件/状态）
        byte[] status = Arrays.copyOfRange(bytes, 0, 80);
        String statusStr = new String(status, "UTF-16BE").trim();
        log.info("status=={}", statusStr);
        // 获取字典配置
        List<DictItem> alarmItems = dictItemService.getByDictCode(DictCodeEnum.FIRE_ALARM_STATUS.getCode());
        Map<String, DictItem> alarmMap = alarmItems.stream().collect(Collectors.toMap(DictItem::getItemCode, v -> v));
        List<DictItem> deviceRecoverItems = dictItemService.getByDictCode(DictCodeEnum.FIRE_RECOVER_DEVICE_STATUS.getCode());
        Map<String, DictItem> deviceRecoverMap = deviceRecoverItems.stream().collect(Collectors.toMap(DictItem::getItemCode, v -> v));
        List<DictItem> hostRecoverItems = dictItemService.getByDictCode(DictCodeEnum.FIRE_RECOVER_HOST_STATUS.getCode());
        Map<String, DictItem> hostRecoverMap = hostRecoverItems.stream().collect(Collectors.toMap(DictItem::getItemCode, v -> v));
        List<DictItem> all = new ArrayList<>();
        all.addAll(alarmItems);
        all.addAll(deviceRecoverItems);
        all.addAll(hostRecoverItems);

        if (!all.stream().map(DictItem::getItemCode).collect(Collectors.toSet()).contains(statusStr)) {
            return;
        }
        // 获取40个字符（设备类型及地址）
        byte[] device = Arrays.copyOfRange(bytes, 80, 160);
        String deviceStr = new String(device, "UTF-16BE").trim();
        log.info("device=={}", deviceStr);
        // 回车换行占用2个字符
        // 获取27个字符（区域映射）
        byte[] area = Arrays.copyOfRange(bytes, 164, 218);
        String areaStr = new String(area, "UTF-16BE").trim();
        log.info("area=={}", areaStr);
        // 获取13个字符（类型编码）
        byte[] type = Arrays.copyOfRange(bytes, 218, 244);
        String typeStr = new String(type, "UTF-16BE").trim();
        log.info("type=={}", typeStr);
        // 获取10个字符（报警时间）
        byte[] time = Arrays.copyOfRange(bytes, 244, 264);
        String timeStr = new String(time, "UTF-16BE").trim();
        log.info("time=={}", timeStr);
        // 获取17个字符（报警日期）
        byte[] date = Arrays.copyOfRange(bytes, 264, 298);
        String dateStr = new String(date, "UTF-16BE").trim();
        log.info("date=={}", dateStr);
        // 获取13个字符（设备地址）
        byte[] deviceCode = Arrays.copyOfRange(bytes, 298, 324);
        String deviceCodeStr = new String(deviceCode, "UTF-16BE").trim();
        log.info("deviceCode=={}", deviceCodeStr);

        DictItem alarmItem = alarmMap.get(statusStr); // 告警字典
        DictItem deviceItem = deviceRecoverMap.get(statusStr); // 设备字典
        DictItem hostItem = hostRecoverMap.get(statusStr);
        // 如果告警中有一些特殊情况，在字典描述中，增加过滤条件
        if (alarmItem != null && StringUtils.isNotBlank(alarmItem.getDescription())) {
            List<String> split = Arrays.stream(alarmItem.getDescription().split(",")).toList();
            for (String s : split) {
                if (deviceStr.contains(s)) {
                    log.info("提醒消息");
                    return;
                }
            }

        }

        // 获取dataTypeId
        String typeId = getTypeId(typeStr);

        // 获取消防设备数据类型
        DeviceTypeEntity deviceTypeEntity = getFireDetectorDeviceTypeEntity();

        // 获取设备信息，如果找不到设备，使用消防主机作为设备的告警设备
        PageResult<DeviceVO<Object>> deviceVOPageResult = getFireDetectorDeviceVOPageResult(deviceCodeStr);
        if (deviceVOPageResult.getTotal() < 1) {
            // 设备未找到，消防主机只有一个，取第一个即可
            deviceVOPageResult = getFireHostDeviceVOPageResult();
            if (deviceVOPageResult.getTotal() < 1) {
                log.error("未配置消防主机");
                throw new NullPointerException("未配置消防主机");
            }
            deviceTypeEntity = getFireHostDeviceTypeEntity();
        }
        DeviceEntity deviceEntity = new DeviceEntity();
        DeviceVO<Object> deviceVO = deviceVOPageResult.getList().get(0);
        BeanUtils.copyProperties(deviceVO, deviceEntity);

        // 判断告警类型
        if (alarmItem != null) { // 告警消息
            // 增加高级记录
            addFireAlarmRecordEvent(statusStr, areaStr, typeStr, timeStr, dateStr, typeId, deviceTypeEntity, deviceEntity);
        } else if (deviceItem != null) {
            // 恢复设备告警
            recoverDeviceFireDetectorAlarm(deviceEntity);
        } else if (hostItem != null) {
            // 主机恢复，恢复所有消防告警
            recoverHostFireAlarm(deviceEntity);
        }

    }

    @Override
    public int removeByDeviceId(String deviceId) {
        LambdaQueryWrapper<AlarmRecordEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AlarmRecordEntity::getDeviceId, deviceId);
        return baseMapper.delete(wrapper);
    }

    /**
     * 恢复主机所有告警记录
     *
     * @param deviceEntity
     */
    private void recoverHostFireAlarm(DeviceEntity deviceEntity) {
        // 修改设备状态为恢复
        deviceEntity.setAlarmStatus(0);
        deviceService.updateBy(deviceEntity);
        // 查询所有消防告警的设备信息
        QueryWrapperX<AlarmRecordEntity> query = new QueryWrapperX<>();
        query.lambda().in(AlarmRecordEntity::getDeviceCode, "FireDetector", "Fire")
                .eq(AlarmRecordEntity::getProcessStatus, GOING.getCode());
        List<AlarmRecordEntity> list = list(query);
        // 修改告警中设备为已处理
        UpdateWrapper<AlarmRecordEntity> updateParam = new UpdateWrapper<>();
        updateParam.lambda().set(AlarmRecordEntity::getProcessStatus, MANUAL_RESET.getCode())
                .in(AlarmRecordEntity::getId, list.stream().map(AlarmRecordEntity::getId).collect(Collectors.toSet()));
        this.update(updateParam);
    }

    /**
     * 恢复设备消防告警
     *
     * @param deviceEntity
     */
    private void recoverDeviceFireDetectorAlarm(DeviceEntity deviceEntity) {
        // 修改设备状态为恢复
        deviceEntity.setAlarmStatus(0);
        deviceService.updateBy(deviceEntity);
        // 修改设备的所有告警为已处理
        UpdateWrapper<AlarmRecordEntity> updateParam = new UpdateWrapper<>();
        updateParam.lambda().set(AlarmRecordEntity::getProcessStatus, MANUAL_RESET.getCode())
                .eq(AlarmRecordEntity::getProcessStatus, GOING.getCode())
                .eq(AlarmRecordEntity::getDeviceId, deviceEntity.getId())
                .eq(AlarmRecordEntity::getDeviceTypeCode, "FireDetector");
        this.update(updateParam);
    }

    private void addFireAlarmRecordEvent(String statusStr, String areaStr, String typeStr, String timeStr, String dateStr, String typeId, DeviceTypeEntity deviceTypeEntity, DeviceEntity deviceEntity) throws ParseException {
        AlarmTypeEntity alarmType = alarmTypeService.getByNameAndAdd(AlarmTypeEnum.FIRE.getInfo());
        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd, yyyy h:mm:ssa", Locale.ENGLISH);
        Date date1 = sdf.parse(dateStr.trim() + " " + timeStr.trim() + "M");
        LocalDateTime dateTime = LocalDateTime.ofInstant(date1.toInstant(), ZoneId.systemDefault());

        AlarmRecordEvent event = AlarmRecordEvent.builder()
                .deviceCode(deviceEntity.getCode())
                .deviceTypeCode(deviceTypeEntity.getId())
                .devicePropertyId(typeId)
                .alarmTime(dateTime)
                .alarmTypeId(alarmType.getId())
                .alarmContent("消防报警，事件：" + statusStr + "，探测器：" + deviceEntity.getName() + "，区域：" + areaStr + "，" + "类型：" + typeStr)
                .deviceId(deviceEntity.getId())
                .build();
        this.add(event);
        deviceEntity.setAlarmStatus(1);
        deviceService.updateBy(deviceEntity);
        AlarmRecordEntity entity = AlarmConvertor.INSTANCE.alarmRecordDTOConvertor2DO(event);
        this.pushAlarmMsgByAlamRule(entity);
    }

    /**
     * 获取消防主机设备类型
     *
     * @return
     */
    private DeviceTypeEntity getFireHostDeviceTypeEntity() {
        DeviceTypeEntity deviceTypeEntity;
        deviceTypeEntity = deviceTypeService.getByCode("Fire");
        if (Objects.isNull(deviceTypeEntity)) {
            DeviceTypeDTO dto = new DeviceTypeDTO();
            dto.setCode("Fire");
            dto.setName("消防主机");
            deviceTypeService.add(dto);
            deviceTypeEntity = deviceTypeService.getByCode("Fire");
        }
        return deviceTypeEntity;
    }

    /**
     * 获取消防主机设备
     *
     * @return
     */
    private PageResult<DeviceVO<Object>> getFireHostDeviceVOPageResult() {
        PageResult<DeviceVO<Object>> deviceVOPageResult;
        DeviceQueryParam paramFire = new DeviceQueryParam();
        paramFire.setType("Fire");
        deviceVOPageResult = deviceService.pageListBy(paramFire);
        return deviceVOPageResult;
    }

    /**
     * 获取消防设备下的设备
     *
     * @param deviceCodeStr
     * @return
     */
    private PageResult<DeviceVO<Object>> getFireDetectorDeviceVOPageResult(String deviceCodeStr) {
        DeviceQueryParam param = new DeviceQueryParam();
        param.setType("FireDetector");
        param.setCode(deviceCodeStr);
        PageResult<DeviceVO<Object>> deviceVOPageResult = deviceService.pageListBy(param);
        return deviceVOPageResult;
    }

    /**
     * 获取消防设备数据类型
     *
     * @return
     */
    private DeviceTypeEntity getFireDetectorDeviceTypeEntity() {
        DeviceTypeEntity deviceTypeEntity = deviceTypeService.getByCode("FireDetector");
        if (Objects.isNull(deviceTypeEntity)) {
            DeviceTypeDTO dto = new DeviceTypeDTO();
            dto.setCode("FireDetector");
            dto.setName("消防探测器");
            deviceTypeService.add(dto);
            deviceTypeEntity = deviceTypeService.getByCode("FireDetector");
        }
        return deviceTypeEntity;
    }

    private String getTypeId(String typeStr) {
        DataTypeEntity typeE = dataTypeService.getByName("消防告警");
        String typeId = null;
        if (Objects.isNull(typeE)) {
            DataTypeDTO dto = new DataTypeDTO();
            dto.setName("消防告警");
            dto.setCode("Fire-warning");
            dto.setGroupNameList(Arrays.asList("消防告警"));
            dataTypeService.add(dto);
            typeId = dataTypeService.getByName(typeStr).getId();
        } else {
            typeId = typeE.getId();
        }
        return typeId;
    }

    private boolean judgeRangeRule(String measureValue, BigDecimal minValue, BigDecimal maxValue) {
        if (StringUtils.isEmpty(measureValue)) {
            return false;
        }
        //满足一个条件即报警
        Boolean maxFlag = judgeMaxValue(measureValue, maxValue);
        Boolean minFlag = judgeMinValue(measureValue, minValue);
        return maxFlag && minFlag;
    }

    /**
     * 判断上限，小于等于上限报警
     *
     * @param measureValue
     * @param maxValue
     * @return
     */
    private boolean judgeMaxValue(String measureValue, BigDecimal maxValue) {
        if (Objects.isNull(maxValue)) {
            return true;
        }
        BigDecimal bigMeasureValue = new BigDecimal(measureValue);
        return bigMeasureValue.compareTo(maxValue) <= 0;
    }

    /**
     * 判断下限，大于等于下限报警
     *
     * @param measureValue
     * @param minValue
     * @return
     */
    private boolean judgeMinValue(String measureValue, BigDecimal minValue) {
        if (Objects.isNull(minValue)) {
            return true;
        }
        BigDecimal bigMeasureValue = new BigDecimal(measureValue);
        return bigMeasureValue.compareTo(minValue) >= 0;
    }

    @Override
    public void storageAlarmDataList(List<MeasureDataBean> list) {
        for (MeasureDataBean gasAlarm : list) {
            DeviceEntity deviceEntity = deviceService.getById(gasAlarm.getDeviceId());
            if (Objects.isNull(deviceEntity)) {
                log.error("[可燃气体报警记录]:设备：{}不存在", gasAlarm.getDeviceId());
                return;
            }

            AlarmRecordEntity entity = new AlarmRecordEntity();
            entity.setDeviceId(gasAlarm.getDeviceId());
            entity.setDeviceCode(deviceEntity.getCode());
            entity.setDeviceTypeCode(deviceEntity.getTypeCode());
            entity.setDeviceDataTypeId(gasAlarm.getDataTypeId());
            //暂时
            AlarmTypeEntity alarmType = alarmTypeService.getByNameAndAdd(AlarmTypeEnum.GAS.getInfo());
            entity.setAlarmTypeId(alarmType.getId());
            entity.setAlarmTime(gasAlarm.getMeasureTime());
            entity.setAlarmContent(deviceEntity.getName() + "监控侦测到" + GasAlarmStatusEnum.getByCode(gasAlarm.getMeasureValue()));
            entity.setAlarmValue(new BigDecimal(gasAlarm.getMeasureValue()));
            entity.setProcessStatus(0);
            save(entity);
        }
    }

    private AlarmTypeService alarmTypeService;

    @Autowired
    public void setAlarmTypeService(AlarmTypeService alarmTypeService) {
        this.alarmTypeService = alarmTypeService;
    }

    private KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    public void setKafkaTemplate(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    private DeviceService deviceService;

    @Autowired
    public void setDeviceService(DeviceService deviceService) {
        this.deviceService = deviceService;
    }

    private AlarmProcessRecordService processRecordService;

    @Autowired
    public void setProcessRecordService(AlarmProcessRecordService alarmProcessRecordService) {
        this.processRecordService = alarmProcessRecordService;
    }

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    private DevicePropertyService devicePropertyService;

    @Autowired
    public void setDevicePropertyService(DevicePropertyService devicePropertyService) {
        this.devicePropertyService = devicePropertyService;
    }


    private static ChartYAxisVo buildEmptyChartYAxisVo(Integer size, String name) {
        ChartYAxisVo chartYAxisVo = new ChartYAxisVo();
        chartYAxisVo.setName(name);
//        chartYAxisVo.setType(type);
//        chartYAxisVo.setStack(id);
        List<BigDecimal> emptyDataList = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            emptyDataList.add(BigDecimal.ZERO);
        }
        chartYAxisVo.setData(emptyDataList);
        return chartYAxisVo;
    }
}
